package laplacianGrowthUtils;

import java.util.ArrayList;
import java.util.Random;

public class ListeCandidats3D {
	protected ArrayList<Grid3DPotential> lesCandidats= new ArrayList<Grid3DPotential>(); 
	private double potentiels[]; 
	private double nu; 
	// radius of the charge
	private double R=0.5; 
	private Random gen=new Random(); 
	private ArrayList<Grid3DPotential> dejaVu=new ArrayList<Grid3DPotential>();
	private double limitMinus,limitPlus; 
	
	
	public void setBounds(double a,double b){
		limitMinus=a; 
		limitPlus=b; 
	}
	
	
	public ListeCandidats3D(double nu,double r) {
		super();
		this.nu = nu;
		this.R=r; 
		
	}
	
	public void addSeedNeutral(Grid3DPotential p){
		dejaVu.add(p); 
	}
	
	/** Ajouter le premier point, charger ses voisins et calculer leur potentiel */
	public void addSeed(Grid3DPotential p){
		dejaVu.add(p); 
		for(int i=-1;i<2;i++)
			for(int j=-1;j<2;j++)
				for(int k=-1;k<2;k++)
				if((i!=0)||(j!=0)||(k!=0)){ // ajouter les 26 points a la liste des candidats
					Grid3DPotential nouvo=new Grid3DPotential(i+p.getX(), j+p.getY(),k+p.getZ());
					this.add(nouvo); 
				}	
		
	}
	
	private void initPotentiel(Grid3DPotential n){
		double potar=0; 
		for(Grid3DPotential e:dejaVu){
			potar+=1-R/Grid3DPotential.distance(n, e); 
			
		}
	
		n.setPotential(potar); 
	}
	
	public void add(Grid3DPotential g){
		if((!this.lesCandidats.contains(g))&&(!this.dejaVu.contains(g)))
		{
		 this.lesCandidats.add(g); 
		 initPotentiel(g);
		}
	}
	/* return a random point, 
	 * remove it from the list of candidates,
	 * according to the probabilities
	 * described in the paper
	 */
	public Grid3DPotential roulette(){
		//for(Grid2DPotential f:lesCandidats) System.out.println(f+" "+f.getPotential()); 
		// trouver max et min
		double phiMin=lesCandidats.get(0).getPotential(); 
		double phiMax=lesCandidats.get(0).getPotential();
		for(Grid3DPotential f:lesCandidats){
			if(f.getPotential()<phiMin) phiMin=f.getPotential(); 
			if(f.getPotential()>phiMax) phiMax=f.getPotential(); 
		}
		
			
		// calculer les potentiels puissance n
		double pTotal=0;
		potentiels=new double[this.getSize()]; 
		//Normaliser les potentiels
		double denom=(phiMax-phiMin); 
		for(int i=0;i<this.getSize();i++){
			Grid3DPotential cur=lesCandidats.get(i); 
		    potentiels[i]=(cur.getPotential()-phiMin)/denom; 
		    potentiels[i]=Math.pow(potentiels[i],nu); 
		    pTotal+=potentiels[i];
		}
		
	
		//System.out.println("--->"+pTotal);
		// Normaliser
		//for(int i=0;i<this.getSize();i++)System.out.println(i+" "+potentiels[i]); 
		potentiels[0]=potentiels[0]/pTotal; 
		
		for(int i=1;i<this.getSize();i++){
			//System.out.print("Avant "+potentiels[i]); 
			potentiels[i]=potentiels[i-1]+potentiels[i]/pTotal;
			//System.out.println(" Apres "+potentiels[i]); 
		}
		double alea=gen.nextDouble(); 
		int i=0; 
		//System.out.println(alea); 
		
		/*
		for(int j=0;j<potentiels.length;j++)
			System.out.println(j+" **  "+potentiels[j]);
		*/	 
		while(alea>=potentiels[i]) i++; 
		//System.out.println("Choisi : "+i+ " "+potentiels.length); 
		Grid3DPotential r=lesCandidats.get(i);
		maj(r);
		return r; 
	}
	
	/**  Remove point n from the list
	 * compute the new potentials
	 */
	private void maj(Grid3DPotential n){
		if(!this.lesCandidats.remove(n)){System.out.println("element non present"); System.exit(0);} 
		if(dejaVu.contains(n)) return; 
		dejaVu.add(n); 
		// mise a jour des potentiels des candidats deja presents
		for(Grid3DPotential c:this.lesCandidats){
			c.setPotential(c.getPotential()+1-R/Math.pow(Grid3DPotential.distance(c, n),1)); 
		}
		// Ajouter les voisins de n, et initialiser leur potentiel
		int xn=n.getX(); 
		int yn=n.getY(); 
		int zn=n.getZ();
		for(int j=-1;j<2;j++)
			for(int k=-1;k<2;k++)
				for(int l=-1;l<2;l++)
				if((j!=0)||(k!=0)||(l!=0)){
					int jj=j; 
					int kk=k; 
					int ll=l; 
					//if(j!=0) kk=0; 
					//if(k!=0) jj=0;
					
					if((xn+jj>=limitMinus)&&(xn+jj<limitPlus)&&(yn+kk>=limitMinus)&&(yn+kk<limitPlus)){
						this.add(new Grid3DPotential(xn+jj, yn+kk,zn+ll)); 
					}
				}
		
	}
	
	public boolean contains(Grid2DPotential g){
		return this. lesCandidats.contains(g); 
	}
	
	public int getSize(){return this.lesCandidats.size(); }

}
